/*
 * cm_image.h
 *
 *  Created on: 2015年1月8日
 *      Author: chens
 */

#ifndef CM_IMAGE_H_
#define CM_IMAGE_H_
#include <stdio.h>
#include <assert.h>
#include "cm_types.h"

#define USE_DOUBLE

#ifdef USE_DOUBLE
typedef double REAL;
#else
typedef float REAL;
#endif

enum cmimg_depth {
	CMIMG_8U = 0,
	CMIMG_8S,
	CMIMG_16U,
	CMIMG_16S,
	CMIMG_32U,
	CMIMG_32S,
	CMIMG_32F,
	CMIMG_64F
};

static inline int cmimg_depth_bytes(enum cmimg_depth d)
{
	if (d == CMIMG_32F)
		return 4;
	return (int) (0x1 << ((unsigned) d >> 1));
}

/*
 * @brief	表示一副图像的结构体
 */
typedef struct cm_image {
	union {
		int height; // height, or, number of rows of the image
		int rows;
	} h;
	union {
		int width;  // width, or, number of columns of the image
		int cols;
	} w;

	enum cmimg_depth depth; // 深度
	int bytes;	// 每个像素的每个通道用几个字节(8bit)来表示 1、2、4、8
	int channel;	// 每个像素的通道数 [1-4]
	int step;	// equal to (width * depth * channel)

	char** row_data; // auxiliary pointers to accelerate the read/write of the image
			 // no memory is really allocated, use memory in (buf)
			 // data[i][j] is a pixel's gray value in (i)th row and (j)th column
	char* data; // pointer to a block of continuous memory containing the image

	double variance;
	int label;

	// TODO 访问图像数据的方法， 根据depth 不同而不同
//	void (*get_pix)(void *p, int x, int y);
//	int (*total_size)(cm_image *img);
} cm_image, cm_image_header;

static inline int cmimg_bufsize(const cm_image *img)
{
	if (img == NULL)
		return 0;
	return (img->w.cols * img->h.rows * img->bytes * img->channel);
}

static inline void cmimg_release_header(cm_image **img)
{
	if (img == NULL || *img == NULL)
		return;

	free(*img);

	*img = NULL;
}

static inline cm_image * cmimg_create_header(struct cm_size s,
		enum cmimg_depth depth, int channel)
{
	cm_image *img = NULL;
	int i;

	img = (cm_image *) calloc(1, sizeof(cm_image));
	if (img == NULL)
		return NULL;

	img->h.height = s.h;
	img->w.width = s.w;
	img->depth = depth;
	img->bytes = cmimg_depth_bytes(depth);
	img->channel = channel;

	img->step = img->w.width * img->bytes * img->channel;

	img->data = NULL;
	img->row_data = NULL;

	img->variance = 0.0;
	img->label = -1;

	return img;
}

static inline void cmimg_setdata(cm_image * img, char *data)
{
	assert(img != NULL && data != NULL);

	int i;
	img->data = data;
	if (img->row_data == NULL)
		img->row_data = (char **) calloc(img->h.rows, sizeof(char *));

	assert(img->row_data != NULL);

	for (i = 0; i < img->h.rows; i++)
		img->row_data[i] = &(img->data[i * img->step]);
}

/**
 * @brief	创建一副 s 大小的图像，以及图像内部的数据
 * @param	s 指定图像的宽高
 */
static inline cm_image * cmimg_create(struct cm_size s, enum cmimg_depth depth,
		int channel)
{
	cm_image * img = NULL;

	img = cmimg_create_header(s, depth, channel);
	if (img == NULL)
		return NULL;

	char * d = (char *) calloc(1, cmimg_bufsize(img));
	cmimg_setdata(img, d);
	return img;
}

static inline void cmimg_release(cm_image **img)
{
	if (img == NULL || *img == NULL)
		return;

	if ((*img)->data == NULL)
		free((*img)->data);

	if ((*img)->row_data == NULL)
		free((*img)->row_data);

	free(*img);
}

static void cmimg_copy(cm_image *src, cm_image *dst)
{
	assert(src->w.cols == dst->w.cols);
	assert(src->h.rows == dst->h.rows);
	assert(src->depth == dst->depth);
	assert(src->channel == dst->channel);

	memcpy(src->data, dst->data, cmimg_bufsize(src));
	cmimg_setdata(dst, dst->data);

}

#endif /* CM_IMAGE_H_ */
